﻿#include <QTimer>
#include <QCoreApplication>
#include <QDebug>
#include "JZLogManager.h"

JZLogModuleConfig::JZLogModuleConfig()
{
    type = None;
}

//JZLogObjectEvent
int JZLogEvent::EventType = QEvent::registerEventType();
JZLogEvent::JZLogEvent()
    :QEvent((Type)EventType)
{
}

//JZLogObject
QSharedPointer<JZLogObject> JZLogObject::makeLog(int module, int level, QString message)
{
    JZLogObjectPtr log_ptr = JZLogObjectPtr(new JZLogObject());
    log_ptr->module = module;
    log_ptr->level = level;
    log_ptr->time = QDateTime::currentMSecsSinceEpoch();
    log_ptr->message = message;
    return log_ptr;
}


//JZLogManager
static bool g_logEnabled = false;
JZLogManager *JZLogManager::instance()
{
    static JZLogManager inst;
    return &inst;
}

JZLogManager::JZLogManager()
{    
    qRegisterMetaType<JZLogObjectPtr>("LogObjectPtr"); 
    m_timer = new QTimer(this);
    m_timer->start(30 * 3000);
    connect(m_timer, &QTimer::timeout, this, &JZLogManager::onTimeOut);

    JZLogModuleConfig config;
    config.name = "General";
    config.type = JZLogModuleConfig::Console;
    addModule(LogModule_General, config);
}

JZLogManager::~JZLogManager()
{
    flushFile();

    m_timer->stop();
    qDeleteAll(m_modules);
    m_modules.clear();

    g_logEnabled = false;
}

void JZLogManager::init()
{
    g_logEnabled = true;
    for(auto module : m_modules)
    { 
        if (module->config.type & JZLogModuleConfig::File)    
            createFile(module);
    }
    m_flushTime = QDateTime::currentDateTime();
}

void JZLogManager::addObserver(int module, QObject *recv)
{
    if (!m_modules.contains(module))
        return;

    m_modules[module]->recvObjects.push_back(recv);
}

void JZLogManager::removeObserver(int module, QObject *recv)
{
    if (!m_modules.contains(module))
        return;

    m_modules[module]->recvObjects.removeAll(recv);
}

QString JZLogManager::logPath()
{
    return m_logPath;
}

void JZLogManager::setLogPath(QString path)
{
    m_logPath = path;
}

void JZLogManager::addModule(int module,JZLogModuleConfig config)
{
    int idx = m_modules.size();
    JZLogModule *mod = new JZLogModule();
    mod->config = config;
    m_modules[module] = mod;    
}

QString JZLogManager::moduleName(int module)
{
    if (!m_modules.contains(module))
        return QString();

    return m_modules[module]->config.name;
}

JZLogModuleConfig JZLogManager::moduleConfig(int id)
{
    return m_modules[id]->config;
}

void JZLogManager::setModuleConfig(int id, JZLogModuleConfig config)
{
    Q_ASSERT(m_modules.contains(id) && !g_logEnabled);
    m_modules[id]->config = config;       
}

void JZLogManager::createFile(JZLogModule *module)
{
    QString time_str = QDateTime::currentDateTime().toString("yyyyMMdd");
    QString file_path = m_logPath + "/" + module->config.name + "_" + time_str + ".log";
    module->file.setFileName(file_path);
    module->file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
}

void JZLogManager::flushFile()
{  
    auto it = m_modules.begin();
    while(it != m_modules.end())
    {        
        bool new_file = false;

        auto &logs = it.value()->logCache;        
        for (int line = 0; line < logs.size(); line++)
        {
            auto log = logs[line].data();

            QDateTime time = QDateTime::fromMSecsSinceEpoch(log->time);
            QString log_save = time.toString("[yyyy-MM-dd HH:mm:ss zzz] ");
            log_save += log->message + "\n";
            
            if (!new_file && m_flushTime.daysTo(time) > 0)
            {
                it.value()->file.close();
                createFile(it.value());
            }
            it.value()->file.write(log_save.toUtf8());
        }
        logs.clear();

        it++;
    }
    m_flushTime = QDateTime::currentDateTime();
}

void JZLogManager::onTimeOut()
{
    flushFile();
}

void JZLogManager::log(int module_id, int level, const QString &message)
{
    QMutexLocker locker(&m_lock);
    if(!g_logEnabled || !m_modules.contains(module_id))
        return;

    auto module = m_modules[module_id];
    if(module->config.type & JZLogModuleConfig::Console)
        qDebug() << message;

    //JZLogObjectPtr
    JZLogObjectPtr log_ptr = JZLogObjectPtr(new JZLogObject());
    log_ptr->module = module_id;
    log_ptr->level = level;
    log_ptr->time = QDateTime::currentMSecsSinceEpoch();    
    log_ptr->message = message;
    if(module->config.type & JZLogModuleConfig::File)
    {
        module->logCache << log_ptr; 
    }
    
    for(int i = module->recvObjects.size() - 1; i >= 0; i--)
    {
        if(!module->recvObjects[i])
        {
            module->recvObjects.removeAt(i);
            continue;
        }

        JZLogEvent *event = new JZLogEvent();
        event->log = log_ptr;
        qApp->postEvent(module->recvObjects[i],event);
    }
}

void JZLogManager::logFmt(int module_id, int type, const char *format, ...)
{    
    va_list var;
    va_start(var, format);
    QString message = QString::vasprintf(format, var);
    va_end(var);

    return log(module_id,type, message);
}